package com.ljc.db.backend.vm;

import com.google.common.primitives.Bytes;
import com.ljc.db.backend.common.SubArray;
import com.ljc.db.backend.dm.dataItem.DataItem;
import com.ljc.db.backend.utils.Parser;

import java.util.Arrays;

/**
 * VM向上层抽象出entry
 * entry结构：
 * [XMIN] [XMAX] [data]
 * 0 - 7  8 - 15 16 - ~
 */
public class Entry {

    private static final int OF_XMIN = 0;           // 创建该版本的事务编号 的起始位置 0
    private static final int OF_XMAX = OF_XMIN + 8; // 删除该版本的事务编号 的起始位置 8
    // 【有其他事务修改了这个版本的信息，则记录到该位置，当前事务只能读到该位置记录的事务编号之前的版本】
    private static final int OF_DATA = OF_XMAX + 8; // 数据起始位置 16

    private long uid;           // 页号 + 页内偏移量
    private DataItem dataItem;  // 数据项
    private VersionManager vm;

    /**
     * 将 DataItem 封装成 Entry
     */
    public static Entry newEntry(VersionManager vm, DataItem dataItem, long uid) {
        Entry entry = new Entry();
        entry.uid = uid;
        entry.dataItem = dataItem;
        entry.vm = vm;
        return entry;
    }

    /**
     * 根据 uid 读取数据线DataItem并封装成 Entry 返回
     */
    public static Entry loadEntry(VersionManager vm, long uid) throws Exception {
        DataItem di = ((VersionManagerImpl)vm).dm.read(uid);
        return newEntry(vm, di, uid);
    }

    /**
     * 进行事务时，为事务中的当前操作加上版本号
     */
    public static byte[] wrapEntryRaw(long xid, byte[] data) {
        byte[] xmin = Parser.long2Byte(xid);
        byte[] xmax = new byte[8];
        return Bytes.concat(xmin, xmax, data);
    }

    /**
     * 释放该Entry的缓存
     */
    public void release() {
        ((VersionManagerImpl)vm).releaseEntry(this);
    }

    /**
     * 释放该DataItem的缓存
     */
    public void remove() {
        dataItem.release();
    }

    /**
     * 以拷贝的形式返回DataItem中的内容
     */
    public byte[] data() {
        dataItem.rLock();
        try {
            // 获取DataItem中的数据
            SubArray sa = dataItem.data();
            byte[] data = new byte[sa.end - sa.start - OF_DATA];
            // 只将数据部分拷贝到data数组中并返回
            System.arraycopy(sa.raw, sa.start + OF_DATA, data, 0, data.length);
            return data;
        } finally {
            dataItem.rUnLock();
        }
    }

    /**
     * 获取某个DataItem的 XMIN
     */
    public long getXmin() {
        dataItem.rLock();
        try {
            SubArray sa = dataItem.data();
            return Parser.parseLong(Arrays.copyOfRange(sa.raw, sa.start + OF_XMIN, sa.start + OF_XMAX));
        } finally {
            dataItem.rUnLock();
        }
    }

    /**
     * 获取某个DataItem的 XMAX
     */
    public long getXmax() {
        dataItem.rLock();
        try {
            SubArray sa = dataItem.data();
            return Parser.parseLong(Arrays.copyOfRange(sa.raw, sa.start + OF_XMAX, sa.start + OF_DATA));
        } finally {
            dataItem.rUnLock();
        }
    }

    /**
     * 准备对DataItem进行修改，修改其中的 XMAX，记录当前版本修改当前版本的DataItem的事务编号
     */
    public void setXmax(long xid) {
        dataItem.before();
        try {
            SubArray sa = dataItem.data();
            System.arraycopy(Parser.long2Byte(xid), 0, sa.raw, sa.start + OF_XMAX, 8);
        } finally {
            // 将数据项修改操作 和 事务号 写入日志中
            dataItem.after(xid);
        }
    }

    public long getUid() {
        return uid;
    }
}
